

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Set;
import java.util.Vector;
import java.util.Iterator;

import Common.Alphabet;
import Common.Graph;
import Common.Pattern;
import Search.LGSearch;
import Search.LGSearchFindAll;
import Search.LGSearchRegExp;

public class Search {

	/**
	 * The main method for search.
	 *
	 * @param args The command line arguments.
	 */
	public static void main(String[] args) {
		Alphabet a;
		Graph g;
		Pattern p;
		String outFile;
		if(args.length != 4)
			usage();
		else {
			try {
				a = new Alphabet(args[0]);
				p = new Pattern(a.getAlphabet(), args[2]);
				g =	Graph.readFile(a, args[1], false, true);

				outFile = args[3];
				// Debug
				System.out.println("Alphabet: " + a.toString());
				System.out.println("Graph: " + g.toString());
				System.out.println("Pattern: " + p.toString());

				// Is the pattern a regular expression
				if (!p.isRegExp()) {
					String[] patternArray = p.pattern();
					// Before searching the graph check the pattern
					// is a subset of the graph labels
					boolean patternInLabels = true;
					for (int i = 0; i < patternArray.length && patternInLabels; i++) {
						patternInLabels = g.contains(patternArray[i]);
					}
					if (patternInLabels) {

						// The pattern is contained within the labels of the graph
						LGSearch s = new LGSearch(p, g);
						int[] seq = s.Search();
						printSeq(outFile, seq);
						// Find all the sequences of node that can be traversed to find the pattern
						LGSearchFindAll sa = new LGSearchFindAll(p, g);
						Vector<int []> seqAll = sa.Search();
						printAllSeq(outFile, seqAll);
					}
					else {

						// The pattern is not contained within the labels of the graphs
						int[] seq = new int[1];
						seq[0] = -1;
						printSeq(outFile, seq);
					}
				}
				else {
					// Find a set of symbols that must be matched
					Set<String> patternSymbolSet = p.symbolSet();
					boolean patternInLabels = true;
					Iterator<String> si = patternSymbolSet.iterator();
					while (si.hasNext() && patternInLabels) {
						patternInLabels = g.contains(si.next());
					}
					if (patternInLabels) {
						LGSearchRegExp s = new LGSearchRegExp(p, g);
						int [] seq = s.Search();
						printSeq(outFile, seq);
					}
					else {
						// One of the symbols that must be match is not contained within the graph
						int[] seq = new int[1];
						seq[0] = -1;
						printSeq(outFile, seq);
					}
				}
			}
			catch (FileNotFoundException fe) {displayException(fe);}
			catch(IOException ie) {displayException(ie);}
			catch(Exception e) {displayException(e);}
		}
	}

	/**
	 * Print the node sequence that corresponds to the pattern or '-1' if its
	 * impossible to traverse the graph in pattern order
	 *
	 * @param outFile the file to print to
	 * @param seq the sequence array
	 */
	private static void printSeq(String outFile, int[] seq) throws Exception {

		StringBuilder sb = new StringBuilder();

		// Was the sequence found
		if (seq[0] != -1) {
			for (int i = 0; i < seq.length; i++) {
				sb.append(seq[i]);
				if (i != seq.length-1) {
					sb.append(" ");
				}
			}
		}
		else {
			sb.append("-1");
		}
		java.io.BufferedWriter out = new java.io.BufferedWriter(new java.io.FileWriter(outFile));
		out.write(sb.toString());
		out.close();
		// Debug
		System.out.println("Single sequence: " + sb.toString());
	}

	/**
	 * Print the all the node sequences that correspond to the pattern or '-1' if its
	 * impossible to traverse the graph in pattern order
	 *
	 * @param outFile the file to print to
	 * @param seqAll the vector of all sequences
	 */
	private static void printAllSeq(String outFile, Vector<int []> seqAll) throws Exception {

		java.io.BufferedWriter out = new java.io.BufferedWriter(new java.io.FileWriter("All" + outFile));
		StringBuilder sb = new StringBuilder();

		int[] firstSeq = seqAll.firstElement();
		// Was the sequence found
		if (firstSeq[0] != -1) {
			// For each sequence
			for (int i = 0; i < seqAll.size(); i++) {
				// Empty the built string
				sb.delete(0, sb.length());
				// Get the next sequence
				int[] seq = seqAll.elementAt(i);
				// For each symbol in the sequence
				for (int j = 0; j < seq.length; j++) {
					sb.append(seq[j]);
					if (j != seq.length-1) {
						sb.append(" ");
					}
					else {
						out.write(sb.toString());
						// Debug
						System.out.println("All sequences: " + sb.toString());
						if (i != seqAll.size() - 1) {
							out.newLine();
						}
					}
				}
			}
		}
		else {
			sb.append("-1");
			out.write(sb.toString());
			// Debug
			System.out.println("All sequences: " + sb.toString());
		}
		out.close();
	}

	/**
	 * Display LGSearch usage
	 */
	private static void usage() {
		System.out.println("Usage: java LGSearch <Alphabet> <Graph> <Pattern> <Output>");
		System.out.println("Usage: java LGSearch <Alphabet> <Graph> <Pattern> <Output>");
		System.out.println("Where:");
		System.out.println("\t<Alphabet>: The name of the file containing the pattern alphabet");
		System.out.println("\t<Graph>: The name of the file containing the directed graph");
		System.out.println("\t<Pattern>: The name of the file containing the search pattern");
		System.out.println("\t<Output>: The name of the output file listing graph nodes");
		System.out.println("Note:");
		System.out.println("\tValid regular expression are of the form: [{Pattern}\\(\\)\\*]+");
		System.out.println("\tWhere:");
		System.out.println("\t\t{Pattern}: The alphanumeric characters making up the search pattern.");
		System.out.println("\t\t           Each new symbol appears on a new line in <Pattern>");
		System.out.println("\tExample <Pattern>:");
		System.out.println("\t\ta");
		System.out.println("\t\t(");
		System.out.println("\t\tb");
		System.out.println("\t\tc");
		System.out.println("\t\t)");
		System.out.println("\t\t*");
		System.out.println("\t\ta");
	}

	/**
	 * Display the thrown exception
	 *
	 * @param e the throw exception
	 */
	private static void displayException(Exception e) {
		System.out.println("Error: " + e.toString());
		System.out.println("Usage: java LGSearch <Alphabet> <Graph> <Pattern> <Output>");
		System.out.println("Where:");
		System.out.println("\t<Alphabet>: The name of the file containing the pattern alphabet");
		System.out.println("\t<Graph>: The name of the file containing the directed graph");
		System.out.println("\t<Pattern>: The name of the file containing the search pattern");
		System.out.println("\t<Output>: The name of the output file listing graph nodes");
		System.out.println("Note:");
		System.out.println("\tValid regular expression are of the form: [{Pattern}\\(\\)\\*]+");
		System.out.println("\tWhere:");
		System.out.println("\t\t{Pattern}: The alphanumeric characters making up the search pattern.");
		System.out.println("\t\t           Each new symbol appears on a new line in <Pattern>");
		System.out.println("\tExample <Pattern>:");
		System.out.println("\t\ta");
		System.out.println("\t\t(");
		System.out.println("\t\tb");
		System.out.println("\t\tc");
		System.out.println("\t\t)");
		System.out.println("\t\t*");
		System.out.println("\t\ta");
	}
}
